/*******************************************************************************
 * Copyright (c) 2008 Sylvain Sicard.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     Noel De Palma - initial API and implementation
 ******************************************************************************/
package fr.jade.service.application.manager;

import java.util.HashMap;
import java.util.Map;

import org.objectweb.fractal.adl.ADLException;
import org.objectweb.fractal.adl.Factory;
import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.NoSuchInterfaceException;
import org.objectweb.fractal.api.control.ContentController;
import org.objectweb.fractal.api.control.IllegalBindingException;
import org.objectweb.fractal.api.control.IllegalLifeCycleException;
import org.objectweb.fractal.util.Fractal;

import fr.jade.base.JadeBaseComponent;
import fr.jade.fraclite.orb.registry.NamingService;
import fr.jade.service.ManagerService;
import fr.jade.util.FractalUtil;
import fr.jade.util.JadeException;

/**
 * This class launch an application adl file. The adlfile to launch is
 * configured either in the attribute adlFile or either in a property file. The
 * name of the property file is defined by the attribute adlFileProperty. In
 * this latter case, the name of the key is :
 * "deploymentService.applicationLauncher.adlFile". The name of this key is not
 * configurable for now. The adlFileProperty is an argument of
 * ApplicationLauncher.fractal.
 * 
 * @author Noel De Palma
 * @author Sylvain Sicard
 * @contact noel.depalma@inrialpes.fr
 */
public class ApplicationManagerImpl extends JadeBaseComponent implements ManagerService {

  /**
   * The deployer used by this launcher to instanciate the adlfile. (it is an
   * adl factory)
   */
  protected static final String deployerItfName = "deployer";
  private Factory deployer = null;

  protected static final String registryItfName = "registry";
  private NamingService         registry     = null;
  
  final static String[]         itfList      = new String[] {deployerItfName, registryItfName};
  
  /**
   * record the name ant the application root cmp deployed by this apps.
   */
  private HashMap<String, Component> deployedApps = new HashMap<String, Component>(5, 5);

  public ApplicationManagerImpl() {
    super();
    log("Application CONFIGURATOR created");
  }

  /**
   * Basic implementation of BindingController
   */

  @Override
  public String[] listFc() {
    return super.concat(super.listFc(), itfList);
  }

  @Override
  public Object lookupFc(final String cItf) throws NoSuchInterfaceException {
    if (cItf.equals("deployer")) {
      return deployer;
    } else if (cItf.equals("registry")) {
      return registry;
    }
    return super.lookupFc(cItf);
  }

  @Override
  public void bindFc(final String cItf, final Object sItf) throws NoSuchInterfaceException, IllegalBindingException,
      IllegalLifeCycleException {
    if (cItf.equals("deployer")) {
      deployer = (org.objectweb.fractal.adl.Factory) sItf;
    } else if (cItf.equals("registry")) {
      registry = (NamingService) sItf;
    } else
      super.bindFc(cItf, sItf);

  }

  @Override
  public void unbindFc(final String cItf) throws NoSuchInterfaceException, IllegalBindingException,
      IllegalLifeCycleException {
    if (cItf.equals("deployer")) {
      deployer = null;
    } else if (cItf.equals("registry")) {
      registry = null;
    } else
      super.unbindFc(cItf);
  }

  /**
   * Implementation of ManagerService
   */
  public void start(String adlId) throws JadeException {

    Component cmp = (Component) deployedApps.get(adlId);
    if (cmp == null)
      throw new JadeException("ApplicationManager.start : component unkown " + adlId);

    try {
      FractalUtil.getLifeCycleController(cmp).startFc();
    } catch (IllegalLifeCycleException e2) {
      throw new JadeException("ApplicationManager.start : cannot start component" + adlId, e2);
    }
    // try {
    // call standard service
    // if the root composite provide a Ressource itf, start it
    // Interface[] lfItf = Jade.getCmpItf(cmp,"fr.jade.service.Service");
    // if (lfItf != null){
    // if (lfItf.length >1) throw new JadeException("ApplicationManager :
    // multiple Service interface found :"+
    // "cannot start "+adlId);
    // ((Service)lfItf[0]).start();
    // }

    // }catch (Exception e){
    // throw new JadeException("ApplicationManager.start : cannot start
    // component"+
    // adlId,e);
    // }
  }

  /*
   * (non-Javadoc)
   * 
   * @see fr.jade.service.ManagerService#deploy(java.lang.String)
   */
  public void deploy(String adlId) throws JadeException {

    Component myapps = null;

    if (adlId == null)
      throw new JadeException("cannot start application service : " + adlId + " : this application is already deployed");

    log("Deploying " + adlId + " ...");

    try {
      Map<String, Object> context = new HashMap<String, Object>();
      // context.put("bootstrap", Boot.dirty);
      myapps = (Component) deployer.newComponent(adlId, context);
    } catch (ADLException e) {
      throw new JadeException("ApplicationManager cannot deploy " + adlId, e);
    }

    // register the root cmp of the application in the registry
    registry.rebind(adlId, myapps);
    log(adlId + " registered ...\n");

    Component[] fathers;
    try {
      fathers = Fractal.getSuperController(self).getFcSuperComponents();
    } catch (NoSuchInterfaceException e) {
      throw new JadeException("Application Manager must have a super component", e);
    }

    if ((fathers == null))
      throw new JadeException("ApplicationManager must have exactly one super component");

    Component applicationManager = fathers[0];
    ContentController cc;
    try {
      cc = FractalUtil.getOptContentController(applicationManager);
    } catch (NoSuchInterfaceException e) {
      throw new JadeException("Application Manager must have a content controller", e);
    }
    try {
      cc.addFcSubComponent(myapps);
    } catch (Exception e) {
      throw new JadeException("Application Manager cannot add apps as a sub component", e);
    }

    deployedApps.put(adlId, myapps);
    log(" -> " + adlId + " successfully Deployed\n");

    // Component applicationManager = fathers[0];
    // ContentControllerNotification ccn = (ContentControllerNotification)
    // (applicationManager.getFcInterface("content-notification-controller"));
    // Component metaAppManager =
    // ((MetaController)(applicationManager.getFcInterface("reflexive-controller"))).getCmpRef();
    // 	       
    // ContentControllerNotification mccn =
    // ((ContentControllerNotification)(metaAppManager.getFcInterface("content-notification-controller")));

    // Component metaApps
    // =((MetaController)(myapps.getFcInterface("reflexive-controller"))).getCmpRef();

    // ccn.addFcSubComponentNotification(myapps);
    // mccn.addFcSubComponentNotification(metaApps);

  }

  /*
   * (non-Javadoc)
   * 
   * @see fr.jade.service.ManagerService#undeploy(java.lang.String)
   */
  public void stop(String adlId) throws JadeException {
    Component myApp = null;

    if (adlId != null) {
      try {

        // lookup the application in the registry
        // because the ref can be dirty if a node fail.
        // we can avoid this if we have a binding to the applications roots.
        myApp = registry.lookup(adlId);

      } catch (Exception e) {
        throw new JadeException("Cannot lookup application root in registry ", e);
      }

      if (myApp != null) {
        try {
          Fractal.getLifeCycleController(myApp).stopFc();

          // deployedApps.remove(adlId);
          // TODO Remove the application root for the application manager

        } catch (NoSuchInterfaceException ignored) {

        } catch (IllegalLifeCycleException e) {
          throw new JadeException("Cannot stop application in applicationmanager", e);
        }
      } else
        throw new JadeException("Cannot stop application in applicationmanager :"
            + " cannot retrieve application root : " + adlId);

    }

  }

  public void undeploy(String adlId) throws JadeException {
    Component myApp = null;

    if (adlId != null) {
      try {

        // lookup the application in the registry
        // because the ref can be dirty if a node fail.
        // we can avoid this if we have a binding to the applications roots.
        myApp = registry.lookup(adlId);

      } catch (Exception e) {
        throw new JadeException("Cannot lookup application root in registry ", e);
      }

      if (myApp != null) {
        try {

          // Remove the application root for the application manager
          // Component []fathers =
          // Fractal.getSuperController(myself).getFcSuperComponents();
          //    	      
          // if ((fathers==null) || (fathers.length!=1))
          // throw new InstantiationException("ApplicationManager must have
          // exactly one super component");
          //    	       
          // Component applicationManager = fathers[0];
          //    	  
          // //Fractal.getLifeCycleController(applicationManager).stopFc();
          // ContentController ccn =
          // (ContentController)(applicationManager.getFcInterface("content-controller"));
          // ccn.removeFcSubComponent(myApp);
          deployedApps.remove(adlId);

        } catch (Exception e) {
          throw new JadeException("Cannot undeploy application service ", e);
        }
      } else
        throw new JadeException("Cannot undeploy application in applicationmanager :"
            + " cannot retrieve application root : " + adlId);

    }

  }

  /*
   * undeploy all the application previously deployed by the application manager
   * 
   * @see fr.jade.service.ManagerService#undeploy()
   */
  // public void undeploy() throws JadeException {
  //    
  // String [] apps = (String [])(deployedApps.keySet().toArray(new String[0]));
  //    
  // if (apps !=null){
  // for (int i = 0;i<apps.length;i++){
  // //Logger.println("APPLI MANAGER : KILLING "+apps[i]);
  // undeploy(apps[i]);
  // }
  // }
  //}
}
